#ifndef PACKETQUEUE_H
#define PACKETQUEUE_H

#include<mutex>
#include<condition_variable>
#include<queue>
#include"mediabase.h"
#include"dlog.h"
extern "C"{
#include"libavcodec/avcodec.h"
}

typedef struct packet_queue_stats
{
    int audio_nb_packets;//队列中音频包的数量
    int video_nb_packets;//队列中视频包的数量
    int audio_size;//队列中所有音频包的总字节大小
    int video_size;//队列中所有视频包的总字节大小
    int64_t audio_duration;//队列中所有音频加起来可以播放的总时长
    int64_t video_duration;//队列中所有视频加起来可以播放的总时长
}PacketQueueStats;

typedef struct my_avpacket
{
    AVPacket *pkt;
    MediaType media_type;
}MyAVPacket;

class PacketQueue
{
public:
    PacketQueue(double audio_frame_duration, double video_frame_duration):_audio_frame_duration(audio_frame_duration)
                                                                            ,_video_frame_duration(video_frame_duration)
    {
        memset(&_stats, 0, sizeof(PacketQueueStats));
    }
    ~PacketQueue()
    {

    }

    int push(AVPacket *pkt, MediaType media_type)
    {
        if(!pkt)
        {
            LogError("pkt is null");
            return -1;
        }
        if(media_type != E_AUDIO_TYPE && media_type != E_VIDEO_TYPE)
        {
            LogError("media_type:%d is unkonwn", media_type);
            return -1;
        }
        // 线程加锁
        std::lock_guard<std::mutex> lock(_mutex);
        int ret = pushPrivate(pkt, media_type);
        if(ret < 0)
        {
            LogError("pushPrivate failed");
            return -1;
        }
        else
        {
            // 当前队列有数据了，随机通知一个线程，可以获取数据了
            _cond.notify_one();
            return 0;
        }
    }
private:
    int pushPrivate(AVPacket *pkt, MediaType media_type)
    {
        if(_abort_request)
        {
            LogWarn("abort requset");
            return -1;
        }
        MyAVPacket *my_pkt = (MyAVPacket *)malloc(sizeof(MyAVPacket));
        if(!my_pkt)
        {
            LogError("malloc MyAVPacket failed");
            return 0;
        }
        my_pkt->pkt = pkt;
        my_pkt->media_type = media_type;
        if(E_AUDIO_TYPE == media_type)
        {
            _stats.audio_nb_packets++;
            _stats.audio_size += pkt->size;
            // _stats 中的 duration 数据不使用pkt->duration来计算,而是根据pts来计算
            _audio_back_pts = pkt->pts;
            // 如果当前队列只有一个包，那么这个包的pts就是队列中的第一个音频的pts
            if(_stats.audio_nb_packets == 1)
            {
                _audio_front_pts = pkt->pts;
            }
        }
        else if(E_VIDEO_TYPE == media_type)
        {
            _stats.video_nb_packets++;
            _stats.video_size += pkt->size;
            // _stats 中的 duration 数据不使用pkt->duration来计算,而是根据pts来计算
            _video_back_pts = pkt->pts;
            // 如果当前队列只有一个包，那么这个包的pts就是队列中的第一个音频的pts
            if(_stats.video_nb_packets == 1)
            {
                _video_front_pts = pkt->pts;
            }
        }
        _queue.push(my_pkt);
        return 0;
    }
public:
    // 取出队列的第一个包数据，同时获取这个包的音视频类型
    int pop(AVPacket **pkt, MediaType &media_type)
    {
        if(_abort_request)
        {
            LogWarn("abort request");
            return -1;
        }
        if(!pkt)
        {
            LogError("pkt is null");
            return -1;
        }
        std::unique_lock<std::mutex> lock(_mutex);
        if(_abort_request)
        {
            LogWarn("abort request");
            return -1;
        }
        if(_queue.empty())
        {
            // 等待唤醒，修改状态前需要线notify，否则这里收不到唤醒消息
            _cond.wait(lock, [this] {
                return !_queue.empty() | _abort_request;
            });
        }
        if(_abort_request)
        {
            LogWarn("abort request");
            return -1;
        }
        //开始取数据
        MyAVPacket *my_pkt = _queue.front();
        *pkt = my_pkt->pkt;
        media_type = my_pkt->media_type;
        // 统计信息
        if(E_AUDIO_TYPE == media_type)
        {
            _stats.audio_nb_packets--;
            _stats.audio_size -= (*pkt)->size;
            _audio_front_pts = (*pkt)->pts;
        }
        else if(E_VIDEO_TYPE == my_pkt->media_type)
        {
            _stats.video_nb_packets--;
            _stats.video_size -= (*pkt)->size;
            _video_front_pts = (*pkt)->pts;
        }
        // 释放读出来的包
        _queue.pop();
        free(my_pkt);
        return 0;
    }

    int popWithTimeout(AVPacket **pkt, MediaType &media_type, int timeout)
    {
        if(time <= 0)
        {
            return pop(pkt, media_type);
        }

        if(_abort_request)
        {
            LogWarn("abort request");
            return -1;
        }
        if(!pkt)
        {
            LogError("pkt is null");
            return -1;
        }
        std::unique_lock<std::mutex> lock(_mutex);
        if(_abort_request)
        {
            LogWarn("abort request");
            return -1;
        }
        if(_queue.empty())
        {
            // 等待唤醒，修改状态前需要线notify，否则这里收不到唤醒消息
            _cond.wait_for(lock, std::chrono::milliseconds(timeout) , [this] {
                return !_queue.empty() | _abort_request;
            });
        }
        if(_abort_request)
        {
            LogWarn("abort request");
            return -1;
        }
        //开始取数据
        MyAVPacket *my_pkt = _queue.front();
        *pkt = my_pkt->pkt;
        media_type = my_pkt->media_type;
        // 统计信息
        if(E_AUDIO_TYPE == media_type)
        {
            _stats.audio_nb_packets--;
            _stats.audio_size -= (*pkt)->size;
            _audio_front_pts = (*pkt)->pts;
        }
        else if(E_VIDEO_TYPE == my_pkt->media_type)
        {
            _stats.video_nb_packets--;
            _stats.video_size -= (*pkt)->size;
            _video_front_pts = (*pkt)->pts;
        }
        // 释放读出来的包
        _queue.pop();
        free(my_pkt);
        return 0;
    }

    bool empty()
    {
        std::lock_guard<std::mutex> lock(_mutex);
        return _queue.empty();
    }

    // 停止所有操作
    void abort()
    {
        std::lock_guard<std::mutex> lock(_mutex);
        _abort_request = true; //需要在另一个重启的地方置为false
        _cond.notify_all(); //通知所有处于等待的线程
    }

    // all:true;清空队列中的所有数据
    // all：false；清理队列中的数据，直到队列中剩余时间小于等于 remain_max_duration ，继续判断，直到遇到I帧才结束。【对视频来说，如果I帧没了，那么这个I帧后面的P、B帧都没用了】
    // 音频数据不需要保存，因为音频数据太小了，一会就传输过去了
    int drop(bool all, int64_t remain_max_duration)
    {
        if(_abort_request)
        {
            LogWarn("abort request");
            return -1;
        }
        std::lock_guard<std::mutex> lock(_mutex);
        while(!_queue.empty())
        {
            MyAVPacket *my_pkt = _queue.front();
            // 判断是否需要保留剩下的数据 [原来包的flags有这么多属性？]
            if(!all && my_pkt->media_type == E_VIDEO_TYPE && (my_pkt->pkt->flags & AV_PKT_FLAG_KEY))
            {
                int64_t duration = _video_back_pts - _video_front_pts; //队列剩余的时长
                // 时长错误，应该是跳帧了或者切帧了
                if(duration < 0 ||
                        duration > _video_frame_duration * _stats.video_nb_packets * 2)
                {
                        LogWarn("_video_back_pts - _video_front_pts:%lld << >> _video_frame_duration * _stats.video_nb_packets:%lld", duration, _video_frame_duration * _stats.video_nb_packets);
                        duration = _video_frame_duration * _stats.video_nb_packets;
                }
                LogInfo("video duration:%lld", duration);
                // 保留 remain_max_duration 时长以内的第一个I帧
                if(duration <= remain_max_duration)
                {
                    break;
                }
            }
            if(E_AUDIO_TYPE == my_pkt->media_type)
            {
                _stats.audio_nb_packets--;
                _stats.audio_size -= my_pkt->pkt->size;
                _audio_front_pts = my_pkt->pkt->pts;
            }
            else if(E_VIDEO_TYPE == my_pkt->media_type)
            {
                _stats.video_nb_packets--;
                _stats.video_size -= my_pkt->pkt->size;
                _video_front_pts = my_pkt->pkt->pts;
            }
            // 释放包数据
            av_packet_free(&my_pkt->pkt);
            _queue.pop();
            free(my_pkt);
        }
        return 0;
    }

    int getAudioDuration()
    {
        std::lock_guard<std::mutex> lock(_mutex);
        int64_t duration = _audio_back_pts - _audio_front_pts;  //以pts为准
        // 也参考帧（包）持续 *帧(包)数
        if(duration < 0     // pts回绕
               || duration > _audio_frame_duration * _stats.audio_nb_packets * 2) {
            LogWarn("_audio_back_pts - _audio_front_pts:%lld << >> _audio_frame_duration * _stats.audio_nb_packets:%lld", duration, _audio_frame_duration * _stats.audio_nb_packets);
            duration =  _audio_frame_duration * _stats.audio_nb_packets;
        } else {
            duration += _audio_frame_duration; // 第一帧 - 第一帧 = 0，故需要多加一个间隔时间[因为实际上有一帧]
        }
        return duration;
    }

    int getVideoDuration()
    {
        std::lock_guard<std::mutex> lock(_mutex);
        int64_t duration = _video_back_pts - _video_front_pts;  //以pts为准
        // 也参考帧（包）持续 *帧(包)数
        if(duration < 0     // pts回绕
               || duration > _video_frame_duration * _stats.video_nb_packets * 2) {
            LogWarn("_video_back_pts - _video_front_pts:%lld << >> _video_frame_duration * _stats.video_nb_packets:%lld", duration, _video_frame_duration * _stats.video_nb_packets);
            duration =  _video_frame_duration * _stats.video_nb_packets;
        } else {
            duration += _video_frame_duration; // 第一帧 - 第一帧 = 0，故需要多加一个间隔时间[因为实际上有一帧]
        }
        return duration;
    }

    int getAudioPackets()
    {
        std::lock_guard<std::mutex> lock(_mutex);
        return _stats.audio_nb_packets;
    }

    int getVideoPackets()
    {
        std::lock_guard<std::mutex> lock(_mutex);
        return _stats.video_nb_packets;
    }

    void getStats(PacketQueueStats *stats)
    {
        if(!stats)
        {
            LogError("stats is null");
            return ;
        }
        std::lock_guard<std::mutex> lock(_mutex);
        int64_t audio_duration = _audio_back_pts - _audio_front_pts;  //以pts为准
        // 也参考帧（包）持续 *帧(包)数
        if(audio_duration < 0     // pts回绕
               || audio_duration > _audio_frame_duration * _stats.audio_nb_packets * 2) {
            LogWarn("_audio_back_pts - _audio_front_pts:%lld << >> _audio_frame_duration * _stats.audio_nb_packets:%lld", audio_duration, _audio_frame_duration * _stats.audio_nb_packets);
            audio_duration =  _audio_frame_duration * _stats.audio_nb_packets;
        } else {
            audio_duration += _audio_frame_duration; // 第一帧 - 第一帧 = 0，故需要多加一个间隔时间[因为实际上有一帧]
        }
        int64_t video_duration = _video_back_pts - _video_front_pts;  //以pts为准
        // 也参考帧（包）持续 *帧(包)数
        if(video_duration < 0     // pts回绕
               || video_duration > _video_frame_duration * _stats.video_nb_packets * 2) {
            LogWarn("_video_back_pts - _video_front_pts:%lld << >> _video_frame_duration * _stats.video_nb_packets:%lld", video_duration, _video_frame_duration * _stats.video_nb_packets);
            video_duration =  _video_frame_duration * _stats.video_nb_packets;
        } else {
            video_duration += _video_frame_duration; // 第一帧 - 第一帧 = 0，故需要多加一个间隔时间[因为实际上有一帧]
        }
        stats->audio_duration = audio_duration;
        stats->video_duration = video_duration;
        stats->audio_nb_packets = _stats.audio_nb_packets;
        stats->video_nb_packets = _stats.audio_nb_packets;
        stats->audio_size = _stats.audio_size;
        stats->video_size = _stats.video_size;
    }

private:
    std::mutex _mutex;
    std::condition_variable _cond;
    std::queue<MyAVPacket *> _queue;

    bool _abort_request = false;//请求退出

    //统计信息
    PacketQueueStats _stats;
    double _audio_frame_duration = 23.21995649; // 一帧音频的播放时长，默认23.2ms，44.1kHz，1024*1000ms/44100=23.21995649ms
    double _video_frame_duration = 40;          // 一帧视频的播放时长，默认40ms，帧率为25帧，1000ms / 25 = 40ms
    // 记录pts
    int64_t _audio_front_pts = 0;//队列中的音频第一帧的pts
    int64_t _audio_back_pts = 0;//队列中的音频最后一帧的pts
    int64_t _video_front_pts = 0;//队列中的视频第一帧的pts
    int64_t _video_back_pts = 0;//队列中的视频最后一帧的pts
};

#endif // PACKETQUEUE_H
